/*-+-*****************************************************************************
 Copyright (C) 2009-2012 Oceanside Software Corporation (R)
 Dallas, Texas

 Author       : Jason T. Brower
 Purpose      : This file is part of a point-of-sale software package
                originally written for Table-Service Restaurants.
              
 Date         : July 8th of 2012
              
 History      : The application that this software file belongs to was 
                written over a 3-Year period.  The first version was written
                in C# .Net 3.5 using Windows Forms.  Being new to .Net at
                the time, I soon learned of Windows Presentation Foundation,
                liked the graphics capabilities and databinding so I
                migrated to WPF.  When the .Net 4.0 framework was released
                the application was migrated from 3.5 to 4.0. Now today,
                .Net 4.5 RC is out and I was already in process of migrating
                before I made this public.
                
Excuse Me     : Please excuse the coding style, it is not representative of
                my typical work.  During the three years I was under a very
                tight budget, constantly running out of funding and was 
                working 18 hour days hyped up on 5-Hour Energy.  The 
                design is not textbook.  It is monolythic because of
                previous issues I will not get into.  There are few comments
                in the code and the ones that are there may be incorrect.
                
What it is not: This is not the exact version that I took through PA-DSS 2.0
                nor is it the exact version running at my customer sites.
                I removed some details for security purposes including some
                encryption algorithms.  The reporting was removed because
                I was including a third party library that cannot be 
                published with this Open Source.
                
What it is    : This application has been running at high volume restaraunts
                for over a year.  It never freezes, crashes or does anything
                unexpected.  So long as it is built, installed and configured
                correctly, it is a strong system that we can all enhance.

Contact       : Please note that I can no longer work for free.  However, you
                can reach me at information@oceansidesw.com.                                                      

*********************************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Printing;
#if _LOCALDB
  using Oceanside.LocalDB;
#else
using Oceanside.RemoteDB;
#endif
namespace Oceanside
{
    public static class SystemMonitor
    {

        public static Int64 _idOfReportRequester;
        public const Int64 CONST_REBOOT_SYSTEM_FLAG = 1;

        private static bool hasCheckedVersion = false;

        private const int CONST_NEWVERSIONSAFE_TIME = 10000;
        private const int CONST_SELF_DESTRUCT_CHECK_RATE = 5000;
        private const int CONST_REBOOT_CHECK_RATE = 5000;
        private const int CONST_SHORTEST_CHECK_RATE = CONST_REBOOT_CHECK_RATE;

        private static long lastSelfDestructCheckTick = Environment.TickCount;
        private static long lastPrinterCheckTick = Environment.TickCount;
        private static long lastRebootCheckTick = Environment.TickCount;
        private static long uptimeStart = Environment.TickCount;


        private static string CONST_NEW_PACKET = "C:\\ProgramData\\.588E83E9-9CD2-44ED-A7FC-26F430A4439E\\packetnew.osa";
        private static string CONST_NEW_VERSION = "C:\\ProgramData\\.588E83E9-9CD2-44ED-A7FC-26F430A4439E\\versionnew.osv";
        private static string CONST_PACKET = "C:\\ProgramData\\.588E83E9-9CD2-44ED-A7FC-26F430A4439E\\packet.osa";
        private static string CONST_VERSION = "C:\\ProgramData\\.588E83E9-9CD2-44ED-A7FC-26F430A4439E\\version.osv";

        private const string CONST_COMPANY_PATH = "\\Oceanside Software Corporation\\Oceanside POS";

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private static string GetProgramDirectoryPath()
        {
            return Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86) + CONST_COMPANY_PATH;
        }

        /// <summary>
        /// 
        /// </summary>
        public static void RelaunchAsyncElevated()
        {
            SHELLEXECUTEINFO shExecInfo = new SHELLEXECUTEINFO();
            string dir = GetProgramDirectoryPath();

            shExecInfo.lpDirectory = dir;
            shExecInfo.lpVerb = "runas";
            shExecInfo.lpFile = dir + "\\" + "oceansidepos.exe";
            shExecInfo.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(shExecInfo);
            ShellExecuteEx(ref shExecInfo);
            Environment.Exit(0);
        }

        /// <summary>
        /// 
        /// </summary>
        public static void RestartSystem()
        {
            try
            {
#if !_LOCALDB
                System.Diagnostics.Process.Start("shutdown.exe", "-r -t 5 -f -c \"Oceanside is now rebooting your system.\"");
#endif
            }
            catch (Exception ex)
            {
                Logging.LogException(ex);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="kiosk"></param>
        /// <param name="flag"></param>
        public static void ClearSystemFlag(KIOSK kiosk, Int64 flag)
        {
            using (DominicaEntities de = new DominicaEntities(DominicaEntities.GetEfConString()))
            {
                var q = from n in de.KIOSKS where n.Id == kiosk.Id select n;
                if (q.Any())
                {
                    KIOSK myNode = q.First();
                    if ((myNode.SystemComFlags & flag) == flag)
                    {
                        q.First().SystemComFlags ^= flag;
                        de.SaveChanges();
                    }
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="kiosk"></param>
        /// <param name="flag"></param>
        public static void SetSystemFlagForAllNodes(KIOSK excludeNode, Int64 flag)
        {
            using (DominicaEntities de = new DominicaEntities(DominicaEntities.GetEfConString()))
            {
                var q = from n in de.KIOSKS where n.Id != excludeNode.Id select n;
                if (q.Any())
                {
                    foreach (KIOSK otherNode in q)
                    {
                        otherNode.SystemComFlags ^= flag;
                    }
                    de.SaveChanges();
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public static void HealthMonitorTask()
        {
            while (true)
            {
                try
                {
                    long tickNow = Environment.TickCount;

#if !_DEBUG
                    if (tickNow - lastSelfDestructCheckTick >= CONST_SELF_DESTRUCT_CHECK_RATE)
                    {
                        if (!IsProcessRunning("oceansidepos"))
                        {
                            Logging.LogMessage("Program Exiting Due to Parent Application Exiting");
                            PosWindow.MainPosWindow.Dispatcher.Invoke(new Action(PosWindow.MainPosWindow.Close));
                        }
                    }
#endif

                    if (!hasCheckedVersion)
                    {
                        if (tickNow - uptimeStart > CONST_NEWVERSIONSAFE_TIME)
                        {
                            hasCheckedVersion = true;

                            if (!NotifyingSwRev.Swrev.Equals(Constants.VersionInRegistry))
                            {
                                if (File.Exists(CONST_NEW_PACKET) && File.Exists(CONST_NEW_VERSION))
                                {
                                    File.Copy(CONST_NEW_PACKET, CONST_PACKET, true);
                                    File.Copy(CONST_NEW_VERSION, CONST_VERSION, true);

                                    File.Delete(CONST_NEW_PACKET);
                                    File.Delete(CONST_NEW_VERSION);
                                    Constants.SetRegistryVersionRev(NotifyingSwRev.Swrev);

                                }
                                else
                                {
#if !_DEBUG
                                    Logging.LogMessage("Registry version mismatch, but unable to find update files.");
#endif
                                }
                            }

                            try
                            {
                                using (DominicaEntities context = new DominicaEntities(DominicaEntities.GetEfConString()))
                                {
                                    KIOSK.Get(context).SwRevision = NotifyingSwRev.Swrev;
                                    context.SaveChanges();
                                }
                            }
                            catch (Exception e)
                            {
                                Logging.LogException(e);
                            }

                            try
                            {
                                ///Update the test db.
                                using (DominicaEntities context = new DominicaEntities(DominicaEntities.GetEfConString(true)))
                                {
                                    KIOSK.Get(context).SwRevision = NotifyingSwRev.Swrev;
                                    context.SaveChanges();
                                }
                            }
                            catch (Exception e)
                            {
                                Logging.LogException(e);
                            }
                        }
                    }
                    if (tickNow - lastPrinterCheckTick >= Constants.PrintQueueTimeout_MS &&
                        !(PosWindow.ActiveWindowInstance is ScreenSaver || PosWindow.ActiveWindowInstance is LoginWindow))
                    {
                        List<QueuedPrintJob> qList = QueuedPrintJob.UpdateQueueStatus();
                        List<string> namesList = new List<string>();
                        List<string> printerStatusMessagesList = new List<string>();

                        if (qList.Count() != 0)
                        {
                            string message = "There are stale print jobs that have exceeded the timeout.  Please attend to printer";
                            if (qList.Count > 1)
                            {
                                message += "s ";
                            }
                            else
                            {
                                message += " ";
                            }

                            for (int i = 0; i < qList.Count(); i++)
                            {
                                QueuedPrintJob qpj = qList[i];
                                if (!namesList.Contains(qpj.PrintServer + qpj.PrinterName))
                                {
                                    if (qpj.PrintServer != null)
                                    {
                                        message += qpj.PrinterName + " on " + qpj.PrintServer.Substring(2);
                                    }
                                    else
                                    {
                                        message += qpj.PrinterName;
                                    }

                                    PrintQueue pq = QueuedPrintJob.GetPrinterStatus(qpj.PrinterPath);
                                    if (pq != null)
                                    {
                                        string pStatusMessage = "Status of " + qpj.PrinterName + ".\n\n";
                                        pStatusMessage += "     IsInError = " + pq.IsInError + "\n";
                                        printerStatusMessagesList.Add(pStatusMessage);
                                    }
                                    namesList.Add(qpj.PrintServer + qpj.PrinterName);

                                    if (i == (qList.Count - 1))
                                    {
                                        message += ".  ";
                                    }
                                    else
                                    {
                                        message += " and ";
                                    }
                                }
                            }
                            message += "Assure the printer has paper and is not currently indicating an error.";
                            Logging.LogMessage(message);
                            UMD.ShowGuiMessageSTASafe_NOTHROW(message);

                            ///The null check is here because somehow and someway we hit this once in a blue moon
                            ///and the list is null.  More than likely a threading issue or related to the debugger.
                            if (printerStatusMessagesList != null && printerStatusMessagesList.Count != 0)
                            {
                                foreach (string s in printerStatusMessagesList)
                                {
                                    UMD.ShowGuiMessageSTASafe_NOTHROW(s);
                                }
                            }
                        }
                        lastPrinterCheckTick = Environment.TickCount;
                    }

                    if (tickNow - lastRebootCheckTick >= CONST_REBOOT_CHECK_RATE)
                    {
                        lastRebootCheckTick = Environment.TickCount;
                        using (DominicaEntities context = new DominicaEntities(DominicaEntities.GetEfConString()))
                        {
                            KIOSK k = KIOSK.Get(context);

                            if ((k != null && (k.SystemComFlags & CONST_REBOOT_SYSTEM_FLAG) != 0))
                            {
                                if (k.Name != null && !k.Name.Equals("KIOSK-0") && !k.Name.Equals("KIOSK-75") 
                                    && !k.Name.Equals("KIOSK-80") && !k.Name.Equals("KIOSK-73"))
                                {
                                    ClearSystemFlag(KIOSK.Get(context), CONST_REBOOT_SYSTEM_FLAG);

                                    if (!Constants.IsTrainingMode && !Constants.IsUsingDevelopmentBuild)
                                    {
                                        RestartSystem();
                                    }
                                }
                            }
                        }
                    }
                    System.Threading.Thread.Sleep(CONST_SHORTEST_CHECK_RATE);
                }
                catch (Exception exc)
                {
                    Constants.ErrorDispatcher.Notify(Error.EType.E_PLACEHOLDER, exc);
                }
            }
        }

        private static bool IsProcessRunning(string name)
        {
            foreach (Process clsProcess in Process.GetProcesses())
            {
                if (clsProcess.ProcessName.Contains(name))
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="lpExecInfo"></param>
        /// <returns></returns>
        [DllImport("shell32.dll", CharSet = CharSet.Auto)]
        static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo);

        /// <summary>
        /// 
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct SHELLEXECUTEINFO
        {
            public int cbSize;
            public uint fMask;
            private IntPtr hwnd;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpVerb;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpFile;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpParameters;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpDirectory;
            public int nShow;
            private IntPtr hInstApp;
            private IntPtr lpIDList;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpClass;
            private IntPtr hkeyClass;
            public uint dwHotKey;
            private IntPtr hIcon;
            private IntPtr hProcess;
        }

        /// <summary>
        /// 
        /// </summary>
        public enum ShowCommands : int
        {
            SW_HIDE = 0,
            SW_SHOWNORMAL = 1,
            SW_NORMAL = 1,
            SW_SHOWMINIMIZED = 2,
            SW_SHOWMAXIMIZED = 3,
            SW_MAXIMIZE = 3,
            SW_SHOWNOACTIVATE = 4,
            SW_SHOW = 5,
            SW_MINIMIZE = 6,
            SW_SHOWMINNOACTIVE = 7,
            SW_SHOWNA = 8,
            SW_RESTORE = 9,
            SW_SHOWDEFAULT = 10,
            SW_FORCEMINIMIZE = 11,
            SW_MAX = 11
        }
    }
}
